import Events from '@/config/events';
type SYSTEM = Events['SYSTEM'];
type APP = Events['APP'];

Object.defineProperty(window, '__EventEmitter_addEventListener__', {
    value: <Name extends keyof SYSTEM>(params: { name: Name, extra?: SYSTEM[Name]['extra'] }, frmName: string) => {
        const _this = EventEmitter as any;
        let callbacks = _this.callbacks[params.name] || [];
        let frmCallbacks = _this.frmCallbacks[params.name];
        const scriptCallbackName = `__EventEmitter_addEventListener_${params.name}_callback__`;
        if (!frmCallbacks) {
            frmCallbacks = {};
            _this.frmCallbacks[params.name] = frmCallbacks;
        }
        const frms = Object.keys(frmCallbacks);
        if (frms.length === 0) {
            api.addEventListener(params, (ret) => {
                Object.keys(frmCallbacks).forEach((key) => {
                    frmCallbacks[key].callback(ret);
                });
            });
        }
        let target = frmCallbacks[frmName];
        if (!target) {
            target = {
                callback: (ret: any) => {
                    Object.keys(frmCallbacks).forEach((key) => {
                        if (key !== '__win__') {
                            api.execScript({
                                name: api.winName,
                                frameName: key,
                                script: `${scriptCallbackName}(${JSON.stringify(ret)})`
                            })
                        } else {
                            for (let i = 0, len = callbacks.length; i < len; i++) {
                                const result = callbacks[i](ret);
                                if (result === false) {
                                    return;
                                }
                            }
                        }
                    });
                },
                num: 0
            }
            frmCallbacks[frmName] = target;
        }
        target.num++;

    }
});
Object.defineProperty(window, '__EventEmitter_removeEventListener__', {
    value: <Name extends keyof SYSTEM>(params: { name: Name }, frmName: string) => {
        const _this = EventEmitter as any;
        let frmCallbacks = _this.frmCallbacks[params.name];
        const target = frmCallbacks[frmName];
        if (target) {
            target.num--;
            if (target.num <= 0) {
                delete frmCallbacks[frmName];
            }
        }

        if (Object.keys(frmCallbacks).length === 0) {
            api.removeEventListener({
                name: params.name
            });
        }
    }
})


export default class EventEmitter {
    private static callbacks: { [event: string]: Array<(args: any) => false | void> } = {};
    private static frmCallbacks: { [event: string]: { [frmName: string]: { callback: (ret: any) => void, num: number } } } = {}

    /**
     * 监听系统事件
     * @param params 
     * @param callback 
     */

    public static onSystem<Name extends keyof SYSTEM>(params: { name: Name, extra?: SYSTEM[Name]['extra'] }, callback: (data: SYSTEM[Name]['data']) => false | void) {
        const frmName = api.frameName;
        const eventName = params.name;
        const scriptCallbackName = `__EventEmitter_addEventListener_${params.name}_callback__`;
        let callbacks = this.callbacks[eventName];
        if (!callbacks) {
            callbacks = [];
            this.callbacks[eventName] = callbacks;
        }
        if (frmName) {
            if (callbacks.length === 0) {
                Object.defineProperty(window, scriptCallbackName, {
                    value: (ret: any) => {
                        for (let i = 0, len = callbacks.length; i < len; i++) {
                            const result = callbacks[i](ret);
                            if (result === false) {
                                return;
                            }
                        }
                    },
                    writable: true
                })
                api.execScript({
                    name: api.winName,
                    script: `__EventEmitter_addEventListener__(${JSON.stringify(params)},"${frmName}")`
                });
            }
        } else {
            let frmCallbacks = this.frmCallbacks[params.name];
            if (!frmCallbacks) {
                frmCallbacks = {};
                this.frmCallbacks[params.name] = frmCallbacks;
            }
            const frms = Object.keys(frmCallbacks);
            if (frms.length === 0) {
                api.addEventListener(params, (ret) => {
                    Object.keys(frmCallbacks).forEach((key) => {
                        frmCallbacks[key].callback(ret);
                    });
                });
            }
            let target = frmCallbacks['__win__'];
            if (!target) {
                target = {
                    callback: (ret) => {
                        Object.keys(frmCallbacks).forEach((key) => {
                            if (key !== '__win__') {
                                api.execScript({
                                    name: api.winName,
                                    frameName: key,
                                    script: `${scriptCallbackName}(${JSON.stringify(ret)})`
                                })
                            } else {
                                for (let i = 0, len = callbacks.length; i < len; i++) {
                                    const result = callbacks[i](ret);
                                    if (result === false) {
                                        return;
                                    }
                                }
                            }
                        });
                    },
                    num: 0
                }
                frmCallbacks['__win__'] = target;
            }
            target.num++;
        }
        callbacks.push(callback);



    }

    /**
     * 移除监听
     */
    public static unSystem<SName extends keyof SYSTEM>(params: { name: SName }, callback?: ((args: SYSTEM[SName]['data']) => void)) {
        const events = this.callbacks[params.name];
        if (!events) {
            return;
        }
        if (callback) {
            const index = events.indexOf(callback);
            events.splice(index, 1);
        } else {
            events.splice(0, events.length);
            delete this.callbacks[params.name];
        }
        const frmName = api.frameName;
        const eventName = params.name;
        let frmCallbacks = this.frmCallbacks[eventName];
        if (frmName) {
            api.execScript({
                name: api.winName,
                script: `__EventEmitter_removeEventListener__(${JSON.stringify(params)},"${frmName}")`
            })
        } else {
            const target = frmCallbacks['__win__'];
            if (target) {
                target.num--;
                if (target.num <= 0) {
                    delete frmCallbacks['__win__'];
                }
            }

            if (Object.keys(frmCallbacks).length === 0) {
                api.removeEventListener({
                    name: params.name
                });
            }
        }


    }


    /**
     * 监听系统事件执行后 移除监听
     * @param params 
     * @param callback 
     */
    public static oneSystem<Name extends keyof SYSTEM>(params: { name: Name, extra?: SYSTEM[Name]['extra'] }, callback: (data: SYSTEM[Name]['data']) => false | void) {
        const fun = (arg: any) => {
            this.unSystem(params, fun);
            return callback(arg);
        }
        this.onSystem(params, fun)
    }

    /**
     * 监听自定义事件  
     * @param params 
     * @param callback 
     */
    public static on<T extends keyof APP>(params: { name: T }, callback: (data: APP[T]['data']) => false | void) {
        let events = this.callbacks[params.name];
        if (!events) {
            events = [];
            this.callbacks[params.name] = events;
            api.addEventListener(params, async (ret) => {
                const arg = ret.value;
                for (let i = 0, len = events.length; i < len; i++) {
                    const result = events[i](arg);
                    if (result === false) {
                        return;
                    }
                }
            });

        }
        events.push(callback);

    }
    /**
     * 监听自定义事件执行后 移除监听
     * @param params 
     * @param callback 
     */
    public static one<T extends keyof APP>(params: { name: T }, callback: (data: APP[T]['data']) => false | void) {
        const fun = (arg: any) => {
            this.un(params, fun);
            return callback(arg);
        }
        this.on(params, fun)
    }

    /**
     * 移除监听
     */
    public static un<CName extends keyof APP>(params: { name: CName }, callback?: ((args: APP[CName]['data']) => void)) {
        const events = this.callbacks[params.name];
        if (!events) {
            return;
        }
        if (callback) {
            const index = events.indexOf(callback);
            events.splice(index, 1);
        } else {
            events.splice(0, events.length);
            delete this.callbacks[params.name];
        }
        if (events.length === 0) {
            api.removeEventListener({
                name: params.name
            });
        }

    }






    /**
     * 发送自定义事件
     */
    public static emit<Name extends keyof APP>(params: { name: Name, data?: APP[Name]['data'] }) {
        api.sendEvent({
            name: params.name,
            extra: params.data
        });
    }
    /**
     * 获取监听事件的方法数组
     * @param name 
     */
    public static getListener<SName extends keyof SYSTEM, CName extends keyof APP>(name: SName | CName) {
        let num = 0;
        const frmCallbacks = this.frmCallbacks[name] || {};
        Object.keys(frmCallbacks).forEach(key => {
            num += frmCallbacks[key].num;
        });
        return num;
    }





}



